package vistas;

import javax.swing.*;

import api.API;
import api.Regla.ObjetoAfectado;
import api.Respuesta;
import api.Suceso;
import api.ControlReflection;
import api.ControlReflection.Metodo;
import api.ControlDispositivos;
import api.Regla;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import api.Dispositivo;

@SuppressWarnings("serial")
public class VentanaReglas extends JFrame implements MouseListener,ActionListener{

	//Componentes del panelSuperior
	private JTextField descripcion;
	private JCheckBox checkConsecutivos;
	private JCheckBox checkEnOrden;
	private JCheckBox checkCancelados;
	private DefaultComboBoxModel modelCBDispEmisor;
	private DefaultComboBoxModel modelCBSucesos;
	private DefaultComboBoxModel modelCBDispAfectado;
	private DefaultComboBoxModel modelCBMetodos;
	
	//Componentes del panelReglaActual
	private DefaultListModel modelListSucesos;
	private DefaultListModel modelListAfectados;
	
	//Componentes del panelReglas
	private DefaultListModel modelListReglas;
	private JList listReglas;
	private Regla reglaActual;
	
	private ControlReflection cr = new ControlReflection();
	
	public VentanaReglas(){
		super("Reglas");
		setPanelPrincipal();
		agregarPanelSuperior();
		agregarPanelReglaActual();
		agregarPanelReglas();
		this.pack();
		this.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
	}

	private void setPanelPrincipal() {
		JPanel panelPrincipal = new JPanel();
		panelPrincipal.setLayout(new GridBagLayout());
		this.setContentPane(panelPrincipal);
	}

	private void agregarPanelSuperior() {
		JPanel panelSuperior = new JPanel();
		panelSuperior.setLayout(new GridBagLayout());
		//JLabels para los textos
		JLabel labelDescripcion = new JLabel("Descripcion:");
		JLabel labelConfig = new JLabel("Configuracion:");
		JLabel labelDispEmisor = new JLabel("Dispositivo emisor");
		JLabel labelSucesos = new JLabel("Sucesos");
		JLabel labelDispAfectado = new JLabel("Dispositivo afectado");
		JLabel labelMetodos = new JLabel("Metodos");
		
		//JTextField para la descripcion
		descripcion = new JTextField();
		
		//JCheckBoxs para las opciones
		checkConsecutivos = new JCheckBox("Informativa");
		checkEnOrden = new JCheckBox("Critica");
		checkCancelados = new JCheckBox("Cancelados");
		
		//JComboBoxs para seleccionar las cosas y los cargo
		this.modelCBDispEmisor = new DefaultComboBoxModel();
		JComboBox cbDispEmisor = new JComboBox(this.modelCBDispEmisor);
		cbDispEmisor.addActionListener(this);
		this.cargarDispositivos(this.modelCBDispEmisor);
		
		this.modelCBSucesos = new DefaultComboBoxModel();
		JComboBox cbSucesos = new JComboBox(this.modelCBSucesos);
		this.cargarSucesos((Dispositivo)this.modelCBDispEmisor.getSelectedItem());
		
		this.modelCBDispAfectado = new DefaultComboBoxModel();
		JComboBox cbDispAfectado = new JComboBox(this.modelCBDispAfectado);
		cbDispAfectado.addActionListener(this);
		this.cargarDispositivos(this.modelCBDispAfectado);
		
		this.modelCBMetodos = new DefaultComboBoxModel();
		JComboBox cbMetodos = new JComboBox(this.modelCBMetodos);
		this.cargarMetodos((Dispositivo)this.modelCBDispAfectado.getSelectedItem());
		
		//JButtons para agregar las cosas seleccionadas a las listas
		JButton agregarSuceso = new JButton("Agregar"){
			//Pongo que hacer cuando se hace clic
			protected void processMouseEvent(MouseEvent event){
				if (event.getID() == MouseEvent.MOUSE_CLICKED){
					this.doClick();
					Dispositivo d = (Dispositivo)modelCBDispEmisor.getSelectedItem();
					Suceso s = (Suceso)modelCBSucesos.getSelectedItem();
					Object[] sucesosObj = modelListSucesos.toArray();
					int i = 0;
					boolean encontrado = false;
					while(i < sucesosObj.length && !encontrado){
						if (s.equals(sucesosObj[i])) encontrado = true;
						i++;
					}
					if (!encontrado) {
						modelListSucesos.addElement(s);
					}
				}
			}
		};
		
		JButton agregarMetodo = new JButton("Agregar"){
			//Pongo que hacer cuando se hace clic
			protected void processMouseEvent(MouseEvent event){
				if (event.getID() == MouseEvent.MOUSE_CLICKED){
					this.doClick();
					Dispositivo dispAfectado = (Dispositivo)modelCBDispAfectado.getSelectedItem();
					Metodo metodoInvocado = (Metodo)modelCBMetodos.getSelectedItem();
					if (dispAfectado != null && metodoInvocado != null){
						ObjetoAfectado objetoNuevo = new Regla().new ObjetoAfectado(dispAfectado.getId(), dispAfectado.getNotificador(), metodoInvocado);
						Object[] objetos = modelListAfectados.toArray();
						int i = 0;
						boolean encontrado = false;
						while(i < objetos.length && !encontrado){
							ObjetoAfectado oa = (ObjetoAfectado)objetos[i];
							if (objetoNuevo.equals(oa)) encontrado = true;
							i++;
						}
						if (!encontrado) modelListAfectados.addElement(objetoNuevo);
					}
				}
			}
		};
		
		//Agrego las cosas al panelSuperior
		GridBagConstraints con = new GridBagConstraints();
		con.anchor = GridBagConstraints.CENTER;
		con.gridy = 0;
		con.gridx = 0;
		con.gridwidth = 3;
		panelSuperior.add(labelDescripcion,con);
		con.gridx = 3;
		panelSuperior.add(labelConfig,con);
		
		con.gridy = 1;
		con.gridx = 0;
		con.fill = GridBagConstraints.HORIZONTAL;
		panelSuperior.add(descripcion,con);
		con.gridx = 3;
		con.gridwidth = 1;
		panelSuperior.add(checkConsecutivos,con);
		con.gridx = 4;
		panelSuperior.add(checkEnOrden,con);
		con.gridx = 5;
		//panelSuperior.add(checkCancelados,con);
		
		con.gridy = 2;
		con.gridx = 0;
		panelSuperior.add(labelDispEmisor,con);
		con.gridx = 1;
		panelSuperior.add(labelSucesos,con);
		con.gridx = 3;
		panelSuperior.add(labelDispAfectado,con);
		con.gridx = 4;
		panelSuperior.add(labelMetodos,con);
		
		con.gridy = 3;
		con.gridx = 0;
		panelSuperior.add(cbDispEmisor,con);
		con.gridx = 1;
		panelSuperior.add(cbSucesos,con);
		con.gridx = 2;
		panelSuperior.add(agregarSuceso,con);
		con.gridx = 3;
		panelSuperior.add(cbDispAfectado,con);
		con.gridx = 4;
		panelSuperior.add(cbMetodos,con);
		con.gridx = 5;
		panelSuperior.add(agregarMetodo,con);
		
		//Agrego el panel a la ventana
		con.gridx = 0;
		con.gridy = 0;
		this.getContentPane().add(panelSuperior,con);
	}

	private void agregarPanelReglaActual() {
		JPanel panelReglaActual = new JPanel();
		panelReglaActual.setLayout(new GridBagLayout());
		
		//JLabels para los textos
		JLabel labelLSucesos = new JLabel("Sucesos necesarios:");
		JLabel labelLMetodos = new JLabel("Objeto afectado - Metodo invocado:");
		
		//Listas de cosas
		this.modelListSucesos = new DefaultListModel();
		JList listSucesos = new JList(modelListSucesos);
		
		this.modelListAfectados = new DefaultListModel();
		JList listMetodos = new JList(modelListAfectados);
		
		//JButton para guardar la regla
		JButton agregarRegla = new JButton("Agregar regla"){
			//Pongo que hacer cuando se hace clic
			protected void processMouseEvent(MouseEvent event){
				if (event.getID() == MouseEvent.MOUSE_CLICKED){
					
					this.doClick();
					boolean valida = true;
					if (descripcion.getText().compareTo("") == 0) valida = false;
					if (modelListSucesos.size() == 0) valida = false;
					if (modelListAfectados.size() == 0) valida = false;

					if (valida){
						//armo la respuesta con descrpcion, necesarios y booleanos
						List<Suceso> necesarios = new ArrayList<Suceso>();
						Object[] sucesosObj = modelListSucesos.toArray();
						for (int i = 0; i < sucesosObj.length; i++){
							necesarios.add((Suceso)sucesosObj[i]);
						}
						Respuesta resp = new Respuesta(descripcion.getText(), necesarios, false, false, false);
//						Respuesta resp = new Respuesta(descripcion.getText(),necesarios,
//								checkEnOrden.isSelected(),checkConsecutivos.isSelected(),checkCancelados.isSelected());
						
						//armo la lista de dispositivos afectados
						ArrayList<ObjetoAfectado> afectados = new ArrayList<ObjetoAfectado>();
						Object[] afectadosObj = modelListAfectados.toArray();
						for (int i = 0; i < afectadosObj.length; i++){
							afectados.add((ObjetoAfectado)afectadosObj[i]);
						}
						
						//agrego la regla a la lista de reglas
						Regla regla = new Regla(resp,afectados);
						if (checkConsecutivos.isSelected()) {
							regla.setImportancia("Informativa");
						}else if (checkEnOrden.isSelected()){
							regla.setImportancia("Critica");
						}
						
						modelListReglas.addElement(regla);
						
						
						API.obtenerInstancia().registrarRegla(regla);
						
						//limpio las listas de sucesos, dispositivos y metodos
						limpiar();
					}
				}
			}
		};
		
		//JButton para cancelar la regla que se esta creando
		JButton cancelarRegla = new JButton("Cancelar"){
			//Pongo que hacer cuando se hace clic
			protected void processMouseEvent(MouseEvent event){
				if (event.getID() == MouseEvent.MOUSE_CLICKED){
					this.doClick();
					limpiar();
				}
			}
		};		
		
		//JButton para borrar la regla seleccionada
		JButton borrarRegla = new JButton("Borrar seleccionada"){
			//Pongo que hacer cuando se hace clic
			protected void processMouseEvent(MouseEvent event){
				if (event.getID() == MouseEvent.MOUSE_CLICKED){
					this.doClick();
					if (reglaActual != null){
						modelListReglas.removeElement(reglaActual);
						API.obtenerInstancia().quitarRegla(reglaActual);
						limpiar();
					}
				}
			}
		};
		
		//JButton para activar una regla
		JButton activarRegla = new JButton("Activar Regla"){
			//Pongo que hacer cuando se hace clic
			protected void processMouseEvent(MouseEvent event){
				if (event.getID() == MouseEvent.MOUSE_CLICKED){
					this.doClick();
					if ((reglaActual != null) && !(reglaActual.isActiva())) {
						reglaActual.setActivada(true);						
					}
				}
			}
		};
		
		//JButton para desactivar una regla
		JButton desactivarRegla = new JButton("Desactivar Regla"){
			//Pongo que hacer cuando se hace clic
			protected void processMouseEvent(MouseEvent event){
				if (event.getID() == MouseEvent.MOUSE_CLICKED){
					this.doClick();
					if ((reglaActual != null) && (reglaActual.isActiva())) {
						reglaActual.setActivada(false);						
					}
				}
			}		
		};
		
		//Agrego las cosas al panelReglaActual
		GridBagConstraints con = new GridBagConstraints();
		con.anchor = GridBagConstraints.CENTER;
		con.gridy = 0;
		con.gridx = 0;
		panelReglaActual.add(labelLSucesos,con);
		con.gridx = 1;
		panelReglaActual.add(labelLMetodos,con);
		
		con.gridy = 1;
		con.gridx = 0;
		con.gridheight = 5;
		JScrollPane scrollSucesos = new JScrollPane(listSucesos);
		scrollSucesos.setPreferredSize(new Dimension(200,132));
		panelReglaActual.add(scrollSucesos,con);
		con.gridx = 1;
		JScrollPane scrollMetodos = new JScrollPane(listMetodos);
		scrollMetodos.setPreferredSize(new Dimension(200,132));
		panelReglaActual.add(scrollMetodos,con);
		con.gridheight = 1;
		con.gridx = 2;
		panelReglaActual.add(agregarRegla,con);
		
		con.gridy = 2;
		con.gridx = 2;
		panelReglaActual.add(cancelarRegla,con);
		
		con.gridy = 3;
		con.gridx = 2;
		panelReglaActual.add(activarRegla,con);
		
		con.gridy = 4;
		con.gridx = 2;
		panelReglaActual.add(desactivarRegla,con);
		
		con.gridy = 5;
		con.gridx = 2;
		panelReglaActual.add(borrarRegla,con);
				
		//Agrego el panel a la ventana	
		con.gridx = 0;
		con.gridy = 1;
		this.getContentPane().add(panelReglaActual,con);
	}

	private void agregarPanelReglas() {
		JPanel panelReglas = new JPanel();
		panelReglas.setLayout(new GridBagLayout());
		
		//JLabel para la lista de reglas
		JLabel labelLReglas = new JLabel("Reglas existentes:");
		
		//Lista para poner las reglas que ya existen y las que se van creando
		this.modelListReglas = new DefaultListModel(); 
		this.listReglas = new JList(modelListReglas);
		this.listReglas.addMouseListener(this);
		
		this.cargarReglas();

		//Agrego los componentes al panelReglas
		GridBagConstraints con = new GridBagConstraints();
		con.anchor = GridBagConstraints.CENTER;
		con.gridy = 0;
		con.gridx = 0;
		panelReglas.add(labelLReglas,con);
		
		con.gridy = 1;
		JScrollPane scrollPane = new JScrollPane(listReglas);
		scrollPane.setPreferredSize(new Dimension(500,98));
		panelReglas.add(scrollPane,con);
		
		//Agrego el panel a la ventana
		con.gridx = 0;
		con.gridy = 2;
		this.getContentPane().add(panelReglas,con);
	}

	private void cargarDatosRegla(Regla reglaElegida) {
		this.reglaActual = reglaElegida;
		
		//cargo la descripcion
		this.descripcion.setText(reglaElegida.getDescripcion());
		
		//cargo los booleanos
		Respuesta r = reglaElegida.getRespuesta();
		this.checkCancelados.setSelected(r.verificaCancelados());
		this.checkConsecutivos.setSelected(r.verificaConsecutivos());
		this.checkEnOrden.setSelected(r.verificaOrden());
		
		//cargo los sucesos necesarios
		this.modelListSucesos.removeAllElements();
		List<Suceso> sucesosNecesarios = r.getSucesosNecesarios();
		Iterator<Suceso> itS = sucesosNecesarios.iterator();
		while (itS.hasNext()) modelListSucesos.addElement(itS.next());

		//cargo los objetos afectados
		this.modelListAfectados.removeAllElements();
		List<ObjetoAfectado> objetosAfectados = reglaElegida.getObjetosAfectados();
		Iterator<ObjetoAfectado> itOA = objetosAfectados.iterator();
		while (itOA.hasNext()) modelListAfectados.addElement(itOA.next());
		this.pack();
	}

	private void cargarReglas() {
		List<Regla> reglas = API.obtenerInstancia().getReglas();
		Iterator<Regla> it = reglas.iterator();
		while (it.hasNext()) modelListReglas.addElement(it.next());
	}
	
	//Metodos para cargar el contenido de los ComboBox segun lo que se elige
	private void cargarDispositivos(DefaultComboBoxModel comboBoxModel){		
		Iterator<Dispositivo> itDispositivos = ControlDispositivos.obtenerInstancia().getDispositivosEdificio().iterator();
		Dispositivo d;		
		while (itDispositivos.hasNext()){
			d = itDispositivos.next();
			comboBoxModel.addElement(d);
		}		
	}
	
	private void cargarMetodos(Dispositivo dispAfectado) {
		if (this.modelCBMetodos == null) this.modelCBMetodos = new DefaultComboBoxModel();
		this.modelCBMetodos.removeAllElements();

		if (dispAfectado != null){
			List<Metodo> metodos = this.cr.getMetodosPropios(dispAfectado.getNotificador().getClass(),false);
			Iterator<Metodo> itM = metodos.iterator();
			while (itM.hasNext()) this.modelCBMetodos.addElement(itM.next());
		}
	}
	
	private void cargarSucesos(Dispositivo dispEmisor) {
		if (this.modelCBSucesos == null) this.modelCBSucesos = new DefaultComboBoxModel();
		this.modelCBSucesos.removeAllElements();
		
		if (dispEmisor != null){
			List<Suceso> sucesos = this.cr.getSucesosPosibles(dispEmisor.getNotificador().getClass());
			Iterator<Suceso> itS = sucesos.iterator();
			while (itS.hasNext()) {
				Suceso s = itS.next();
				s.setObjGenerador(dispEmisor.getNotificador());
				this.modelCBSucesos.addElement(s);
			}
		}
	}

	public void mouseClicked(MouseEvent arg0) {
		if (arg0.getID() == MouseEvent.MOUSE_CLICKED){
			if (this.listReglas.getComponentCount() > 0) cargarDatosRegla((Regla)this.listReglas.getSelectedValue());
		}
		this.pack();
	}
	
	private void limpiar(){
		this.reglaActual = null;
		this.descripcion.setText("");
		this.checkCancelados.setSelected(false);
		this.checkEnOrden.setSelected(false);
		this.checkConsecutivos.setSelected(false);
		this.modelListAfectados.removeAllElements();
		this.modelListSucesos.removeAllElements();
		this.pack();
	}

	public void mouseEntered(MouseEvent arg0) {}
	public void mouseExited(MouseEvent arg0) {}
	public void mousePressed(MouseEvent arg0) {}
	public void mouseReleased(MouseEvent arg0) {}

	public void actionPerformed(ActionEvent arg0) {
		DefaultComboBoxModel cbModel = (DefaultComboBoxModel)((JComboBox)arg0.getSource()).getModel();
		if (cbModel == this.modelCBDispEmisor){
			cargarSucesos((Dispositivo)this.modelCBDispEmisor.getSelectedItem());
		} else if (cbModel == this.modelCBDispAfectado){
			cargarMetodos((Dispositivo)this.modelCBDispAfectado.getSelectedItem());
		}
		this.pack();
	}
	
	public void actualizar(){
		this.modelCBDispEmisor.removeAllElements();
		this.cargarDispositivos(this.modelCBDispEmisor);
		this.cargarSucesos((Dispositivo)this.modelCBDispEmisor.getSelectedItem());
		this.modelCBDispAfectado.removeAllElements();
		this.cargarDispositivos(this.modelCBDispAfectado);
		this.cargarMetodos((Dispositivo)this.modelCBDispAfectado.getSelectedItem());
	}
}